/*
 * This file is part of quickReminder, a software for gamers
 *
 * Copyright (C) 2006-2008 Lucas Romero
 *
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses/>.
 */
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text;
using System.Threading;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Diagnostics;

namespace UserNotifier
{
    public class OSDWarnModule : WarnModule
    {
        private BufferedGraphicsContext context;
        private BufferedGraphics grafx;

        private Image m_WarnImage;
        private int m_Spacing = 5;

        public int Spacing
        {
            get { return m_Spacing; }
            set { m_Spacing = value; }
        }
        //private Graphics g = null;
        private string m_WarnMessage = string.Empty;
        private TimerCollection m_Timers = new TimerCollection();
        private IntPtr m_WindowHandle = IntPtr.Zero;

        private bool m_IgnoreWarnings = false;

        public bool IgnoreWarnings
        {
            get { return m_IgnoreWarnings; }
            set { m_IgnoreWarnings = value; }
        }

        private Process m_GameProcess = null;

        public Process GameProcess
        {
            get { return m_GameProcess; }
            set
            { 
                m_GameProcess = value;
                UpdateWarnString();
            }
        }
        StringCollection WarnMessages = new StringCollection();
        private bool m_DrawWarnImage = true;

        public bool DrawWarnImage
        {
            get { return m_DrawWarnImage; }
            set { m_DrawWarnImage = value; }
        }
        private Font m_OSDFont = new Font("Tahoma", 30);

        public Font OSDFont
        {
            get { return m_OSDFont; }
            set { m_OSDFont = value; }
        }
        private Color m_OSDColor = Color.Red;

        public Color OSDColor
        {
            get { return m_OSDColor; }
            set { m_OSDColor = value; }
        }
        private GraphicsPath m_WarnPath = new GraphicsPath();

        public OSDWarnModule(int interval)
            : base(interval)
        {
            System.Resources.ResourceManager res = new System.Resources.ResourceManager("UserNotifier.images", this.GetType().Assembly);
            m_WarnImage = (Image)res.GetObject("messagebox_warning");
            // Retrieves the BufferedGraphicsContext for the 
            // current application domain.
            context = BufferedGraphicsManager.Current;

            // Sets the maximum size for the primary graphics buffer
            // of the buffered graphics context for the application
            // domain.  Any allocation requests for a buffer larger 
            // than this will create a temporary buffered graphics 
            // context to host the graphics buffer.
            context.MaximumBuffer = new Size(1024,768);

            // Allocates a graphics buffer the size of this form
            // using the pixel format of the Graphics created by 
            // the Form.CreateGraphics() method, which returns a 
            // Graphics object that matches the pixel format of the form.
            

        }

        /*public override void InitDelegate()
        {
 	         Thread.CurrentThread.Priority = ThreadPriority.Highest;
        }*/

        /*public override void CleanupDelegate()
        {
            if (g != null)
                g.Dispose();
        }*/

        public override void Warn()
        {
            //DateTime start = DateTime.Now;
            if (m_GameProcess != null)
            {
                if (!m_GameProcess.HasExited)
                {
                    m_GameProcess.Refresh();
                }
                //if (g == null || m_GameProcess.MainWindowHandle != m_WindowHandle)
                //{
                //try
                //{
                    Graphics g = Graphics.FromHwnd(m_GameProcess.MainWindowHandle);
                    
                //}
                //catch { }
                    //Monitor.Enter(g);
                    //m_WindowHandle = m_GameProcess.MainWindowHandle;
                //}
                if (!m_GameProcess.HasExited && g != null)
                {
                    if (grafx == null)
                        grafx = context.Allocate(g, new Rectangle(0, 0, 500, 100));
                    grafx.Render(g);
                    //g.
                    //g.SmoothingMode = SmoothingMode.None;
                    //g.InterpolationMode = InterpolationMode.Low;
                    
                    g.Dispose();
                    //Monitor.Enter(m_WarnPath);
                    
                }
                //Monitor.Exit(g);
            }

            //Trace.WriteLine(DateTime.Now.Subtract(start).TotalMilliseconds.ToString());
        }
        public void AddWarning(string Warning)
        {
            if (m_IgnoreWarnings)
                return;
            if (!WarnMessages.Contains(Warning))
            {
                WarnMessages.Add(Warning);
                UpdateWarnString();
            }
        }
        private void RemoveWarning(object Warning)
        {
            RemoveWarning((string)Warning);
        }
        public void AddWarning(string Warning, int timeout)
        {
            if (m_IgnoreWarnings)
                return;
            AddWarning(Warning);
            m_Timers.NewTimer(Warning, new TimerCallback(RemoveWarning), timeout);
            //timers.Add(new WarnTimer(Warning, new TimerCallback(RemoveWarning), timeout));
        }
        public void RemoveWarning(string Warning)
        {
            if (WarnMessages.Contains(Warning))
            {
                WarnMessages.Remove(Warning);
                UpdateWarnString();
            }
        }
        public void UpdateWarnString()
        {
            if (WarnMessages.Count > 0)
            {
                if (m_IgnoreWarnings)
                    return;
                StringBuilder b = new StringBuilder();
                foreach (String s in WarnMessages)
                {
                    b.Append(s);
                    b.Append('\n');
                }
                m_WarnMessage = b.ToString();
                Monitor.Enter(m_WarnPath);
                m_WarnPath.Reset();
                m_WarnPath.AddString(m_WarnMessage, m_OSDFont.FontFamily, (int)m_OSDFont.Style, m_OSDFont.Size, new Point(0, 0), new StringFormat());
                if (m_DrawWarnImage)
                {
                    Matrix m = new Matrix();
                    m.Translate(m_WarnImage.Width + m_Spacing, 0);
                    m_WarnPath.Transform(m);
                    if (grafx != null)
                        grafx.Graphics.DrawImageUnscaled(m_WarnImage, 0, 0);
                }
                if (grafx != null)
                    grafx.Graphics.FillPath(new SolidBrush(m_OSDColor), m_WarnPath);
                Monitor.Exit(m_WarnPath);
                Start();
            }
            else
            {
                m_WarnMessage = String.Empty;
                Stop();
            }

        }
    }

    public class WarnTimer
    {
        private Timer m_Timer;
        private int m_Timeout;
        private string m_text;
        private TimerCallback callback;
        private TimerCollection m_Parent;
        public string Text
        {
            get
            {
                return m_text;
            }
        }
        public WarnTimer(TimerCollection parent, string text, TimerCallback Callback, int timeout)
        {
            callback = Callback;
            m_Parent = parent;
            m_Timer = new Timer(new TimerCallback(CallbackWrapper), text, timeout, Timeout.Infinite);
            m_text = text;
        }
        public void Reset()
        {
            m_Timer.Change(m_Timeout, Timeout.Infinite);
        }
        private void CallbackWrapper(object text)
        {
            callback(text);
            m_Parent.Remove(this);
        }

        public void Cancel()
        {
            m_Timer.Change(0, Timeout.Infinite);
        }
    };

    public class TimerCollection : CollectionBase
    {
        public void NewTimer(string text, TimerCallback callback, int timeout)
        {
            this.Add(new WarnTimer(this, text, callback, timeout));
        }
        public int Add(WarnTimer item)
        {
            foreach (WarnTimer w in this)
            {
                if (item.Text == w.Text)
                {
                    w.Reset();
                    return IndexOf(w);
                }
            }
            return List.Add(item);
        }
        public void Insert(int index, WarnTimer item)
        {
            foreach (WarnTimer w in this)
            {
                if (item.Text == w.Text)
                {
                    w.Cancel();
                }
            }
            List.Insert(index, item);
        }
        public void Remove(WarnTimer item)
        {
            List.Remove(item);
        }
        public bool Contains(WarnTimer item)
        {
            return List.Contains(item);
        }
        public int IndexOf(WarnTimer item)
        {
            return List.IndexOf(item);
        }
        public void CopyTo(WarnTimer[] array, int index)
        {
            List.CopyTo(array, index);
        }
        public WarnTimer this[int index]
        {
            get { return (WarnTimer)List[index]; }
            set { List[index] = value; }
        }
    }
}
